function getByte(allocator: Box[Allocator]) {
    return allocator.alloc(sizeof Uint8);
}

function testLinearAllocator() {
    printf("testing linear allocator\n");
    var a = LinearAllocator.new(sizeof Uint8 * 2);
    var v1 = a.alloc(sizeof Uint8);
    printf("%s\n", if v1.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());
    var v2 = a.alloc(sizeof Uint8);
    printf("%s\n", if v2.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());
    var v3 = a.alloc(sizeof Uint8);
    printf("%s\n", if v3.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());

    a.reset();
    var v4 = a.alloc(sizeof Uint8);
    match v1 {
        Some(x) => {
            match v4 {
                Some(y) => {
                    if x == y {
                        printf("after reset, they're the same as expected\n");
                    }
                }
            }
        }
    }
}

function testStackAllocator() {
    printf("testing stack allocator\n");
    var a = StackAllocator.new(sizeof Uint8 * 3);
    var v1 = a.alloc(sizeof Uint8);
    printf("%s\n", if v1.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());
    var v2 = a.alloc(sizeof Uint8);
    printf("%s\n", if v2.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());
    var v3 = a.alloc(sizeof Uint8);
    printf("%s\n", if v3.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());
    var v4 = a.alloc(sizeof Uint8);
    printf("%s\n", if v4.isSome() then "got a pointer" else "didn't get a pointer");
    printf("remaining: %zu\n", a.remaining());

    // a.free(v3.unwrap());
    match v3 {
        Some(x) => a.free(x);
    }
    var v5 = a.alloc(sizeof Uint8);
    match v3 {
        Some(x) => {
            match v5 {
                Some(y) => {
                    if x == y {
                        printf("after free, they're the same as expected\n");
                    }
                    a.free(y);
                }
            }
        }
    }

    printf("remaining: %zu\n", a.remaining());var ptr: Ptr[Uint8] = getByte();
    printf("remaining: %zu\n", a.remaining());
    (*ptr) = 1;

    var v6;
    using implicit (a as Box[Allocator]) {
        printf("remaining: %zu\n", a.remaining());
        v6 = getByte();
        printf("remaining: %zu\n", a.remaining());
    }
    match v3 {
        Some(x) => {
            if x == v6 {
                printf("via box, they're the same as expected\n");
            }
        }
    }
}

function testPoolAllocator() {
    printf("testing pool allocator\n");
    var a = PoolAllocator[Int].new(2);
    var i1 = a.alloc(1);
    printf("%s\n", if i1.isSome() then "got a pointer" else "didn't get a pointer");
    var i2 = a.alloc(1);
    printf("%s\n", if i2.isSome() then "got a pointer" else "didn't get a pointer");
    var i3 = a.alloc(1);
    printf("%s\n", if i3.isSome() then "got a pointer" else "didn't get a pointer");

    match i2 {
        Some(x) => {
            a.free(x as Ptr[Void]);
        }
    }
    var i4 = a.alloc(1);
    printf("%s\n", if i4.isSome() then "got a pointer" else "didn't get a pointer");

    match i2 {
        Some(x) => match i4 {
            Some(y) => {
                printf("recycled pointer reused as expected\n");
            }
        }
    }

    var i5 = a.alloc(1);
    printf("%s\n", if i5.isSome() then "got a pointer" else "didn't get a pointer");
}

function main() {
    testLinearAllocator();
    testStackAllocator();
    testPoolAllocator();

    var x: Int = 15;
    var y: Int = 30;

    var xp = &x;
    var yp = &y;

    copy(x, y);
    printf("%i %i\n", x, y);

    y = 20;
    move(y, x);
    printf("%i %i\n", x, y);

    var z: Ptr[Int] = mallocator.calloc(sizeof Int);
    printf("%i\n", *z);
}
